#include <crow/http_response.h>

#include "common/api/client.h"

#include "common/api/result.hpp"
#include "common/elements/user.h"
#include "common/log/crow_logger.hpp"
#include "common/log/log.h"
#include "common/log/user_log.hpp"

#include "common/more/string.h"
#include "common/more/uuid.h"

#include "common/helpers/container_helper.hpp"
#include "common/helpers/context_helper.h"
#include "common/helpers/crow_helper.h"
#include "common/helpers/query_helper.h"

#include "translations.h"
#include "ui_controller.h"

#include "ui_session/ui_session_message.h"
#include "ui_user_handler.h"

namespace
{

crow::mustache::context menuContext(
    const UiSessionPtr session,
    const Elements::UserPtr target = nullptr
)
{
    auto tr = session->translation();
    auto trMainMenu = tr["mainMenu"];
    auto trBreadCrumbs = tr["breadCrumbs"];
    auto trPage = tr["pages"]["user"];
    auto trTabs = trPage["tabs"];

    const auto linkPath = Common::toString("/user/", target ? target->id() : 0);

    // Главное меню.
    Common::ContextList mainMenu;
    mainMenu << Common::ContextValue(
        { { "link", "/users/" },
          { "caption", trMainMenu["users"] },
          { "icon", "ri-user-fill" } }
    );
    mainMenu << Common::ContextValue(
        { { "link", "/roles/" },
          { "caption", trMainMenu["roles"] },
          { "icon", "ri-shield-user-fill" } }
    );
    mainMenu << Common::ContextValue(
        { { "link", "/teams/" },
          { "caption", trMainMenu["teams"] },
          { "icon", "ri-team-fill" } }
    );
    mainMenu << Common::ContextValue(
        { { "link", "/workflows/" },
          { "caption", trMainMenu["workflows"] },
          { "icon", "ri-mind-map" } }
    );
    mainMenu << Common::ContextValue(
        { { "link", "/item_types/" },
          { "caption", trMainMenu["itemTypes"] },
          { "icon", "ri-article-fill" } }
    );

    // Хлебные крошки.
    Common::ContextList breadCrumbs;
    breadCrumbs << Common::ContextValue(
        { { "link", "/users/" },
          { "caption", trBreadCrumbs["users"] } }
    );
    breadCrumbs << Common::ContextValue(
        { { "link", linkPath },
          { "caption", target ? target->fullName() : trBreadCrumbs["newUser"] },
          { "isLast", true } }
    );

    // Вкладки.
    Common::ContextList tabs;
    tabs << Common::ContextValue(
        { { "link", linkPath },
          { "caption", trTabs["properties"] },
          { "isActive", true } }
    );
    // При создании нового пользователя доступна только вкладка со свойствами.
    if (target)
    {
        tabs << Common::ContextValue(
            { { "link", linkPath + "/roles" },
              { "caption", trTabs["roles"] } }
        );
        tabs << Common::ContextValue(
            { { "link", linkPath + "/statistics" },
              { "caption", trTabs["statistics"] } }
        );
        tabs << Common::ContextValue(
            { { "link", linkPath + "/reports" },
              { "caption", trTabs["reports"] } }
        );
    }

    return crow::mustache::context({
        { "tr", trPage },
        { "mainMenu", mainMenu.data() },
        { "breadCrumbs", breadCrumbs.data() },
        { "tabs", tabs.data() },
        { "target", Common::contextFromElement(target) },
        { "sessionMessages", session->sessionMessages().toContextList().data() },
    });
}

} // namespace

namespace Ui
{

void UserHandler::setup(crow::App<>& crowApplication, UiController* controller)
{
    CROW_ROUTE(crowApplication, "/user/<uint>")
        .methods("GET"_method)(
            [controller](const crow::request& request, unsigned userId)
            {
                const Uuids::Uuid sessionUuid = Common::currentSessionUuid(request);
                const auto session = controller->session(sessionUuid);
                if (!session)
                    return Common::redirect("/");

                SESSION_LOG_INFO
                    << "Попытка открыть редактор пользователя.";

                // TODO : Проверить права.

                if (0 == userId)
                {
                    auto context = menuContext(session);
                    return crow::response(controller->newUserPageTemplate().render(context));
                }

                const auto target = session->cashManager().users.element(userId);
                if (!target)
                {
                    SESSION_LOG_WARN
                        << "Пользователь с идентификатором " << userId << " не найден.";
                    return Common::movedPermanently("/error404");
                }

                auto context = menuContext(session, target);
                return crow::response(controller->userPageTemplate().render(context));
            }
        );

    CROW_ROUTE(crowApplication, "/user/<uint>/save")
        .methods("POST"_method)(
            [controller](const crow::request& request, unsigned userId)
            {
                const Uuids::Uuid sessionUuid = Common::currentSessionUuid(request);
                const auto session = controller->session(sessionUuid);
                if (!session)
                    return Common::redirect("/");

                SESSION_LOG_INFO << "Попытка сохранения пользователя.";

                // Получение экземпляра пользователя.
                Elements::UserPtr target = nullptr;
                if (0 == userId)
                {
                    target.reset(new Elements::User());
                }
                else if (target = session->cashManager().users.cloneElement(userId); !target)
                {
                    SESSION_LOG_WARN
                        << "Пользователь с идентификатором " << userId << " не найден.";
                    return Common::movedPermanently("/error404");
                }

                // Назначение параметров пользователя.
                const Common::QueryHelper params(request.body);
                target->setLogin(params.get("login", target->login()));
                target->setFirstName(params.get("firstName", target->firstName()));
                target->setMiddleName(params.get("middleName", target->middleName()));
                target->setLastName(params.get("lastName", target->lastName()));
                target->setEmail(params.get("email", target->email()));
                if (const auto password = params.get("password"); !password.empty())
                    target->setPassword(password);
                target->setNeedChangePassword(params.getBoolean("needChangePassword", target->needChangePassword()));
                target->setIsBlocked(params.getBoolean("isBlocked", target->isBlocked()));
                target->setUiLang(params.get("uiLang", target->uiLang()));

                // Создание нового пользователя.
                if (0 == userId)
                {
                    auto addResult = controller->datenaroGate()->addElement(
                        sessionUuid,
                        target
                    );
                    userId = addResult.m_id;
                    session->forceUpdate();

                    SESSION_LOG_INFO
                        << "Новый пользователь: " << target->login() << " успешно сохранён.";

                    auto trActions = session->translation()["actions"];
                    session->sessionMessages().push(
                        UiSessionMessage(
                            /* Заголовок */
                            trActions["addUser"],
                            /* Текст */
                            (Api::Result::Type::NoAccess == addResult.m_type)
                                ? trActions["noAccess"]
                                : trActions["success"],
                            /* Тип сообщения */
                            addResult.m_isSuccess
                                ? UiSessionMessage::Success
                                : UiSessionMessage::Alert
                        )
                    );
                    return Common::movedPermanently("/user/" + std::to_string(userId));
                }

                // Изменение существующего пользователя.
                const auto setResult = controller->datenaroGate()->setElement(
                    sessionUuid,
                    target
                );
                session->forceUpdate();

                SESSION_LOG_INFO
                    << "Пользователь: " << target->login() << " успешно сохранён.";

                auto trActions = session->translation()["actions"];
                session->sessionMessages().push(
                    UiSessionMessage(
                        /* Заголовок */
                        trActions["editUser"],
                        /* Текст */
                        (Api::Result::Type::NoAccess == setResult.m_type)
                            ? trActions["noAccess"]
                            : trActions["success"],
                        /* Тип сообщения */
                        setResult.m_isSuccess
                            ? UiSessionMessage::Success
                            : UiSessionMessage::Alert
                    )
                );

                return Common::movedPermanently("/user/" + std::to_string(userId));
            }
        );

    CROW_ROUTE(crowApplication, "/user/<uint>/delete")
        .methods("GET"_method, "POST"_method)(
            [controller](const crow::request& request, unsigned userId)
            {
                const Uuids::Uuid sessionUuid = Common::currentSessionUuid(request);
                const auto session = controller->session(sessionUuid);
                if (!session)
                    return Common::redirect("/");

                const auto target = session->cashManager().users.cloneElement(userId);
                if (!target)
                {
                    SESSION_LOG_WARN
                        << "Попытка удаления несуществующего пользователя.";
                    return Common::movedPermanently("/error404");
                }

                SESSION_LOG_INFO
                    << "Удаление пользователя: " << target->login() << ".";

                const auto delResult = controller->datenaroGate()->delElement(
                    sessionUuid,
                    target->elementType(),
                    target->id()
                );
                session->forceUpdate();

                auto trActions = session->translation()["actions"];
                session->sessionMessages().push(
                    UiSessionMessage(
                        /* Заголовок */
                        trActions["removeUser"],
                        /* Текст */
                        (Api::Result::Type::NoAccess == delResult.m_type)
                            ? trActions["noAccess"]
                            : trActions["success"],
                        /* Тип сообщения */
                        delResult.m_isSuccess
                            ? UiSessionMessage::Success
                            : UiSessionMessage::Alert
                    )
                );

                if (!delResult.m_isSuccess)
                {
                    SESSION_LOG_WARN
                        << "Ошибка удаления пользователя: " << target->login() << "."
                        << " Причина ошибки: " << delResult.m_reason << ".";
                    return Common::redirect("/user/" + std::to_string(userId));
                }

                SESSION_LOG_INFO
                    << "Пользователь: " << target->login() << " успешно удалён.";
                return Common::redirect("/users/");
            }
        );
}

} // namespace Ui
